home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 16 / AMIGAplus Sonderheft 16 (1998)(ICP)(DE)[!].iso / pd / anwendungen / ispell-3.1.18src / minrexx.c < prev    next >
C/C++ Source or Header  |  1995-09-13  |  15KB  |  465 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.  This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  */
  21. static char *blurb = "Radical Eye MinRexx 0.4ljr";
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include "config.h"
  26. #include "ispell.h"
  27. #include "proto.h"
  28. #include <inline/exec.h>
  29.  
  30. /*
  31.  *   All of our local globals, hidden from sight.
  32.  */
  33. static struct MsgPort *rexxPort;/* this is *our* rexx port */
  34. static int bringerdown;        /* are we trying to shut down? */
  35. static struct rexxCommandList *globalrcl;    /* our command association list */
  36. static long stillNeedReplies;    /* how many replies are pending? */
  37. static long rexxPortBit;    /* what bit to wait on for Rexx? */
  38. static char *extension;        /* the extension for macros */
  39. static void (*userdisp) (struct RexxMsg *, struct rexxCommandList *, char *);    /* the user's dispatch function */
  40. static struct RexxMsg *oRexxMsg;/* the outstanding Rexx message */
  41. /*
  42.  *   Our library base.  Don't you dare close this!
  43.  */
  44. #if 0
  45. struct RxsLib *RexxSysBase;
  46. #else
  47. struct Library *RexxSysBase;
  48. #endif
  49. /*
  50.  *   This is the main entry point into this code.
  51.  */
  52. long upRexxPort (char *s, struct rexxCommandList *rcl, char *exten, void(*uf)(struct RexxMsg *, struct rexxCommandList *, char *))
  53. /*
  54.  *   The first argument is the name of your port to be registered;
  55.  *   this will be used, for instance, with the `address' command of ARexx.
  56.  *   The second argument is an association list of command-name/user-data
  57.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  58.  *   structure with a NULL in the name field. The commands are case
  59.  *   sensitive.  The user-data field can contain anything appropriate,
  60.  *   perhaps a function to call or some other data.
  61.  *   The third argument is the file extension for ARexx macros invoked
  62.  *   by this program.  If you supply this argument, any `primitive' not
  63.  *   in the association list rcl will be sent out to ARexx for
  64.  *   interpretation, thus allowing macro programs to work just like
  65.  *   primitives.  If you do not want this behavior, supply a `NULL'
  66.  *   here, and those commands not understood will be replied with an
  67.  *   error value of RXERRORNOCMD.
  68.  *   The fourth argument is the user dispatch function.  This function
  69.  *   will *only* be called from rexxDisp(), either from the user calling
  70.  *   this function directly, or from dnRexxPort().  Anytime a command
  71.  *   match is found in the association list, this user-supplied function
  72.  *   will be called with two arguments---the Rexx message that was
  73.  *   received, and a pointer to the association pair.  This function
  74.  *   should return a `1' if the message was replied to by the function
  75.  *   and a `0' if the default success code of (0, 0) should be returned.
  76.  *   Note that the user function should never ReplyMsg() the message;
  77.  *   instead he should indicate the return values with replyRexxCmd();
  78.  *   otherwise we lose track of the messages that still lack replies.
  79.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  80.  *   If something goes wrong, it simply returns a `0'.  Note that this
  81.  *   function is safe to call multiple times because we check to make
  82.  *   sure we haven't opened already.  It's also a quick way to change
  83.  *   the association list or dispatch function.
  84.  */
  85. {
  86.  
  87. /*
  88.  *   Some basic error checking.
  89.  */
  90.   if (rcl == NULL || uf == NULL)
  91.     return (0L);
  92. /*
  93.  *   If we aren't open, we make sure no one else has opened a port with
  94.  *   this name already.  If that works, and the createport succeeds, we
  95.  *   fill rexxPortBit with the value to return.
  96.  *
  97.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  98.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  99.  */
  100.   if (rexxPort == NULL)
  101.     {
  102.       Forbid ();
  103.       if (FindPort (s) == NULL)
  104.     rexxPort = CreateMsgPort ();
  105.       rexxPort->mp_Node.ln_Name = s;
  106.       rexxPort->mp_Node.ln_Pri = 0;
  107.       AddPort (rexxPort);
  108.       Permit ();
  109.       if (rexxPort != NULL)
  110.     rexxPortBit = 1L << rexxPort->mp_SigBit;
  111.     }
  112. /*
  113.  *   Squirrel away these values for our own internal access, and return
  114.  *   the wait bit.
  115.  */
  116.   globalrcl = rcl;
  117.   extension = exten;
  118.   userdisp = uf;
  119.   return (rexxPortBit);
  120. }
  121.  
  122. /*
  123.  *   This function closes the rexx library, but only if it is open
  124.  *   and we aren't expecting further replies from REXX.  It's
  125.  *   *private*, but it doesn't have to be; it's pretty safe to
  126.  *   call anytime.
  127.  */
  128. void closeRexxLib (void)
  129. {
  130.   if (stillNeedReplies == 0 && RexxSysBase)
  131.     {
  132.       CloseLibrary ((struct Library *)RexxSysBase);
  133.       RexxSysBase = NULL;
  134.     }
  135. }
  136.  
  137. /*
  138.  *   This function closes down the Rexx port.  It is always safe to
  139.  *   call, and should *definitely* be made a part of your cleanup
  140.  *   routine.  No arguments and no return.  It removes the Rexx port,
  141.  *   replies to all of the messages and insures that we get replies
  142.  *   to all the ones we sent out, closes the Rexx library, deletes the
  143.  *   port, clears a few flags, and leaves.
  144.  */
  145. void dnRexxPort (void)
  146. {
  147.   if (rexxPort)
  148.     {
  149.       RemPort (rexxPort);
  150.       bringerdown = 1;
  151. /*
  152.  *   A message still hanging around?  We kill it off.
  153.  */
  154.       if (oRexxMsg)
  155.     {
  156.       oRexxMsg->rm_Result1 = RXERRORIMGONE;
  157.       ReplyMsg ((struct Message *)oRexxMsg);
  158.       oRexxMsg = NULL;
  159.     }
  160.       while (stillNeedReplies)
  161.     {
  162.       WaitPort (rexxPort);
  163.       dispRexxPort ();
  164.     }
  165.       closeRexxLib ();
  166.       DeleteMsgPort (rexxPort);
  167.       rexxPort = NULL;
  168.     }
  169.   rexxPortBit = 0;
  170. }
  171.  
  172. /*
  173.  *   Here we dispatch any REXX messages that might be outstanding.
  174.  *   This is the main routine for handling Rexx messages.
  175.  *   This function is fast if no messages are outstanding, so it's
  176.  *   pretty safe to call fairly often.
  177.  *
  178.  *   If we are bring the system down and flushing messages, we reply
  179.  *   with a pretty serious return code RXERRORIMGONE.
  180.  *
  181.  *   No arguments, no returns.
  182.  */
  183. void dispRexxPort (void)
  184. {
  185.   register struct RexxMsg *RexxMsg;
  186.   register struct rexxCommandList *rcl;
  187.   register char *p;
  188.   register int dontreply;
  189.  
  190. /*
  191.  *   If there's no rexx port, we're out of here.
  192.  */
  193.   if (rexxPort == NULL)
  194.     return;
  195. /*
  196.  *   Otherwise we have our normal loop on messages.
  197.  */
  198.   while (RexxMsg = (struct RexxMsg *) GetMsg (rexxPort))
  199.     {
  200. /*
  201.  *   If we have a reply to a message we sent, we look at the second
  202.  *   argument.  If it's set, it's a function we are supposed to call
  203.  *   so we call it.  Then, we kill the argstring and the message
  204.  *   itself, decrement the outstanding count, and attempt to close
  205.  *   down the Rexx library.  Note that this call only succeeds if
  206.  *   there are no outstanding messages.  Also, it's pretty quick, so
  207.  *   don't talk to me about efficiency.
  208.  */
  209.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
  210.     {
  211.       if (RexxMsg->rm_Args[1])
  212.         {
  213.           (*((int (*) (struct RexxMsg *)) (RexxMsg->rm_Args[1]))) (RexxMsg);
  214.         }
  215.       DeleteArgstring (RexxMsg->rm_Args[0]);
  216.       DeleteRexxMsg (RexxMsg);
  217.       stillNeedReplies--;
  218.       closeRexxLib ();
  219. /*
  220.  *   The default case is we got a message and we need to check it for
  221.  *   primitives.  We skip past any initial tabs or spaces and initialize
  222.  *   the return code fields.
  223.  */
  224.     }
  225.       else
  226.     {
  227.       p = (char *) RexxMsg->rm_Args[0];
  228.       while (*p > 0 && *p <= ' ')
  229.         p++;
  230.       RexxMsg->rm_Result1 = 0;
  231.       RexxMsg->rm_Result2 = 0;
  232. /*
  233.  *   If somehow the reply is already done or postponed, `dontreply' is
  234.  *   set.
  235.  */
  236.       dontreply = 0;
  237. /*
  238.  *   If the sky is falling, we just blow up and replymsg.
  239.  */
  240.       if (bringerdown)
  241.         {
  242.           RexxMsg->rm_Result1 = RXERRORIMGONE;
  243. /*
  244.  *   Otherwise we cdr down our association list, comparing commands,
  245.  *   until we get a match.  If we get a match, we call the dispatch
  246.  *   function with the appropriate arguments, and break out.
  247.  */
  248.         }
  249.       else
  250.         {
  251.           oRexxMsg = RexxMsg;
  252.           for (rcl = globalrcl; rcl->name; rcl++)
  253.         {
  254.           if (cmdcmp (rcl->name, p) == 0)
  255.             {
  256.               if (p[strlen (rcl->name)])
  257.             (*userdisp) (RexxMsg, rcl, p + strlen (rcl->name) + 1);
  258.               else
  259.             (*userdisp) (RexxMsg, rcl, p + strlen (rcl->name));
  260.               break;
  261.             }
  262.         }
  263. /*
  264.  *   If we broke out, rcl will point to the command we executed; if we
  265.  *   are at the end of the list, we didn't understand the command.  In
  266.  *   this case, if we were supplied an extension in upRexxPort, we know
  267.  *   that we should send the command out, so we do so, synchronously.
  268.  *   The synchronous send takes care of our reply.  If we were given a
  269.  *   NULL extension, we bitch that the command didn't make sense to us.
  270.  */
  271.           if (rcl->name == NULL)
  272.         {
  273.           if (extension)
  274.             {
  275.               syncRexxCmd (RexxMsg->rm_Args[0], RexxMsg);
  276.               dontreply = 1;
  277.             }
  278.           else
  279.             {
  280.               RexxMsg->rm_Result1 = RXERRORNOCMD;
  281.             }
  282.         }
  283.         }
  284. /*
  285.  *   Finally, reply if appropriate.
  286.  */
  287.       oRexxMsg = NULL;
  288.       if (!dontreply)
  289.         ReplyMsg ((struct Message *)RexxMsg);
  290.     }
  291.     }
  292. }
  293.  
  294. /*
  295.  *   This is the function we use to see if the command matches
  296.  *   the command string.  Not case sensitive.  Make sure all commands
  297.  *   are given in lower case!
  298.  */
  299. int cmdcmp (char *c, char *m)
  300. {
  301.   while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z'))))
  302.     {
  303.       c++;
  304.       m++;
  305.     }
  306.   if (!(*c) && *m)
  307.     return ((int) *m != ' ');
  308.   return ((int) *c);
  309. }
  310.  
  311. /*
  312.  *   Opens the Rexx library if unopened.  Returns success (1) or
  313.  *   failure (0).  This is another function that is *private* but
  314.  *   that doesn't have to be.
  315.  */
  316. int openRexxLib (void)
  317. {
  318.   if (RexxSysBase)
  319.     return (1);
  320. #if 0
  321.   return ((RexxSysBase = (struct RxsLib *)OpenLibrary (RXSNAME, 0L)) != NULL);
  322. #else
  323.   return ((RexxSysBase = (struct Library *)OpenLibrary (RXSNAME, 0L)) != NULL);
  324. #endif
  325. }
  326.  
  327. /*
  328.  *   This is the general ARexx command interface, but is not the one
  329.  *   you will use most of the time; ones defined later are easier to
  330.  *   understand and use.  But they all go through here.
  331.  */
  332. struct RexxMsg *sendRexxCmd (char *s, void (*f)(struct RexxMsg *), STRPTR p1, STRPTR p2, STRPTR p3)
  333. /*
  334.  *   The first parameter is the command to send to Rexx.
  335.  *   The second parameter is either NULL, indicating that the command
  336.  *   should execute asynchronously, or a function to be called when the
  337.  *   message we build up and send out here finally returns.  Please note
  338.  *   that the function supplied here could be called during cleanup after
  339.  *   a fatal error, so make sure it is `safe'.  This function always is
  340.  *   passed one argument, the RexxMsg that is being replied.
  341.  *   These are up to three arguments to be stuffed into the RexxMsg we
  342.  *   are building up, making the values available when the message is
  343.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  344.  */
  345. {
  346.   register struct MsgPort *rexxport;
  347.   register struct RexxMsg *RexxMsg;
  348.  
  349. /*
  350.  *   If we have too many replies out there, we just return failure.
  351.  *   Note that you should check the return code to make sure your
  352.  *   message got out!  Then, we forbid, and make sure that:
  353.  *      - we have a rexx port open
  354.  *      - Rexx is out there
  355.  *      - the library is open
  356.  *      - we can create a message
  357.  *      - we can create an argstring
  358.  *
  359.  *   If all of these succeed, we stuff a few values and send the
  360.  *   message, permit, and return.
  361.  */
  362.   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
  363.     return (NULL);
  364.   RexxMsg = NULL;
  365.   if (openRexxLib () && (RexxMsg =
  366.       CreateRexxMsg (rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  367.       (RexxMsg->rm_Args[0] = CreateArgstring (s, (long) strlen (s))))
  368.     {
  369.       RexxMsg->rm_Action = RXCOMM;
  370.       RexxMsg->rm_Args[1] = (STRPTR) f;
  371.       RexxMsg->rm_Args[2] = p1;
  372.       RexxMsg->rm_Args[3] = p2;
  373.       RexxMsg->rm_Args[4] = p3;
  374.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR;
  375.       Forbid ();
  376.       if (rexxport = FindPort (RXSDIR))
  377.     PutMsg (rexxport, (struct Message *)RexxMsg);
  378.       Permit ();
  379.       if (rexxport)
  380.     {
  381.       stillNeedReplies++;
  382.       return (RexxMsg);
  383.     }
  384.       else
  385.     DeleteArgstring (RexxMsg->rm_Args[0]);
  386.     }
  387.   if (RexxMsg)
  388.     DeleteRexxMsg (RexxMsg);
  389.   closeRexxLib ();
  390.   return (NULL);
  391. }
  392.  
  393. /*
  394.  *   This function is used to send out an ARexx message and return
  395.  *   immediately.  Its single parameter is the command to send.
  396.  */
  397. struct RexxMsg *asyncRexxCmd (char *s)
  398. {
  399.   return (sendRexxCmd (s, NULL, NULL, NULL, NULL));
  400. }
  401.  
  402. /*
  403.  *   This function sets things up to reply to the message that caused
  404.  *   it when we get a reply to the message we are sending out here.
  405.  *   But first the function we pass in, which actually handles the reply.
  406.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  407.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  408.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  409.  *   along.
  410.  */
  411. void replytoit (struct RexxMsg *msg)
  412. {
  413.   register struct RexxMsg *omsg;
  414.  
  415.   omsg = (struct RexxMsg *) (msg->rm_Args[2]);
  416.   replyRexxCmd (omsg, msg->rm_Result1, msg->rm_Result2, NULL);
  417.   ReplyMsg ((struct Message *)omsg);
  418. }
  419.  
  420. /*
  421.  *   This function makes use of everything we've put together so far,
  422.  *   and functions as a synchronous Rexx call; as soon as the macro
  423.  *   invoked here returns, we reply to `msg', passing the return codes
  424.  *   back.
  425.  */
  426. struct RexxMsg *syncRexxCmd (char *s, struct RexxMsg *msg)
  427. {
  428.   return (sendRexxCmd (s, &replytoit, (STRPTR)msg, NULL, NULL));
  429. }
  430.  
  431. /*
  432.  *   There are times when you want to pass back return codes or a
  433.  *   return string; call this function when you want to do that,
  434.  *   and return `1' from the user dispatch function so the main
  435.  *   event loop doesn't reply (because we reply here.)  This function
  436.  *   always returns 1.
  437.  */
  438. void replyRexxCmd (struct RexxMsg *msg, long primary, long secondary, char *string)
  439. /*
  440.  *   The first parameter is the message we are replying to.
  441.  *   The next two parameters are the primary and secondary return
  442.  *   codes.
  443.  *   The final parameter is a return string.  This string is only
  444.  *   returned if the primary return code is 0, and a string was
  445.  *   requested.
  446.  *
  447.  *   We also note that we have replied to the message that came in.
  448.  */
  449. {
  450. /*
  451.  *   Note how we make sure the Rexx Library is open before calling
  452.  *   CreateArgstring . . . and we close it down at the end, if possible.
  453.  */
  454.   if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT)))
  455.     {
  456.       if (string && openRexxLib ())
  457.     secondary = (long) CreateArgstring (string, (long) strlen (string));
  458.       else
  459.     secondary = 0L;
  460.     }
  461.   msg->rm_Result1 = primary;
  462.   msg->rm_Result2 = secondary;
  463.   closeRexxLib ();
  464. }
  465.